import java.net.*;
import java.io.*;
import java.util.*;


/**
 * Socketed threaded thingie so you can control 2pir from somewhere else
 * 
 * Isn't that a bitchin idea. I hope this works. 
 * 
 * 
 * @author Sam Reese
 *
 */
public class pir_sock extends Thread {
	private pir_io parent;
	private static int PIR_DEFAULT_PORT = 666;
	private ServerSocket servsock;
	private Socket connection; 
	private BufferedReader input;
	private PrintStream output;
	
	
	public pir_sock(pir_io parent_thread, int port){
		parent = parent_thread;
		try{
			servsock = new ServerSocket(port);
		}catch(IOException e){
			System.out.println("failed opening port" + e);
		}
		
	}
	
	public pir_sock(pir_io parent_thread){
		this(parent_thread, PIR_DEFAULT_PORT);
	}
	// sends to the socket the status of a single effect
	public boolean sendStatus(int effect, int stat){
		if (connection == null)
			return false;
		output.println("s:" + effect + ":"+ stat + ":");
		return true;
	}
	
	//parse input.
	//accepted strings are
	//RESET::  resets all to off
	//F:n:b:   change status of effect n to b
	//P:n:t:   turn effect n on for unit time t
	private boolean parseInput(String instr){
		if (pir_io.DEBUG_NET){
			System.out.println("NET: input recieved: "+ instr);
		}
		try{
			StringTokenizer tokens = new StringTokenizer(instr, ":");
			if(tokens.countTokens() < 3){
				return false;
			}
		
		int c = tokens.nextToken().charAt(0);
		switch (c) {
		case 'R': 
		if (pir_io.DEBUG_NET){
				System.out.println("NET: reset recieved");
			}
		    if (reset())
		    	output.println("reset all");
			return true;
		case 'F':
			if (pir_io.DEBUG_NET){
			System.out.println("NET: fire recieved");
			}
	
			// change status 
			try{
			parent.fire(Integer.parseInt(tokens.nextToken()), Integer.parseInt(tokens.nextToken()));
			}catch (Exception e){
				System.out.println("socket input Number format exception, dropped" + e);
				return false;
			}
			return true;
		case 'P':
			if (pir_io.DEBUG_NET){
				System.out.println("NET: pulse recieved");
			}
			int curEffect = Integer.parseInt(tokens.nextToken());
		    parent.fire(curEffect, parent.PIR_HIGH_ON);
		    try{
		    	Thread.sleep(Integer.parseInt(tokens.nextToken()));
		    }catch (InterruptedException ie){
				if (pir_io.DEBUG_NET){
					System.out.println("NET:VeryOdd: " + ie);
				}
			    parent.fire(curEffect, parent.PIR_HIGH_OFF);
		    }
		    parent.fire(curEffect, parent.PIR_HIGH_OFF);
			// perform pulse
		    
		case 'A':
			tokens.nextToken();
			int toSleep = Integer.parseInt(tokens.nextToken());
			if (pir_io.DEBUG_NET){
				System.out.println("NET: pulse ALL recieved");
			}
			for (int i = 1; i <= parent.effects; i++){
			
				parent.fire(i, parent.PIR_HIGH_ON);
			}
		    try{
		    	Thread.sleep(toSleep);
		    }catch (InterruptedException ie){
				if (pir_io.DEBUG_NET){
					System.out.println("NET:VeryOdd: " + ie);
				}
			    
		    }
		    for (int i = 1 ; i <= parent.effects; i++ ){
		    	parent.fire(i, parent.PIR_HIGH_OFF);
		    }
			// perform pulse
		case 'S':
			
			parent.instSequence(Integer.parseInt(tokens.nextToken()), tokens.nextToken());
			}return true;
		
		
		} catch (Exception e){
			System.out.println("Tokenizer failed. bad input" + e);
			
		}
		return false;
	}
	
	//sends to the socket all effects off. 
	public boolean sendReset(){
		if (connection == null)
			return false;
		output.println("RESET::");
		return false;
	}
	
	//controller tells to reset all effects
	public boolean reset(){
		return parent.reset();
	}
	
	public void close(){
		try {
			if (connection != null){
		       output.close();
		       input.close();
		       connection.close();
			}
		       servsock.close();
		       
		    } 
		    catch (IOException e) {
		       System.out.println(e);
		    }

	}
	
	public int getPort(){
		return servsock.getLocalPort();
	}
	
	//now accepting connections and blocking.
	public void run(){
		while(true){
		try{
			connection = servsock.accept();
			if (pir_io.DEBUG_NET){
				System.out.println("NET: Server socket accepted.");
			}
		
			
		}catch (IOException e){
			System.out.println("Failed accepting sock" + e);
		}
		try {
			input = new BufferedReader(new InputStreamReader(connection.getInputStream()));
		}
		catch (IOException e) {
			System.out.println("failure making inputstream" + e);
		}
	    try {
	       output = new PrintStream(connection.getOutputStream());
	    }
	    catch (IOException e) {
	       System.out.println("failure making outputstream" + e);
	    }
	    String line;
        while (connection.isConnected()) {
        	try {
        		line = input.readLine();
        		if (line == null){
        			break;
        		}
        		parseInput(line);
        	}catch(IOException e){
        		if( e.toString().equals("java.io.IOException: Stream closed")){
        			break;
        		}
        		System.out.println("Reading socket input failed" + e);
        		if (e.equals("java.net.SocketException")){
        			break;
        		}
        	}
        }
        try{
        connection.close();
        }catch (IOException e){
        	System.out.println("Error closing socket" + e);
        }
		}// end while loop
	}
}
